#include "cvt.h"
#include "sscvt_win/cvt_win_export.h"
#include <string.h>

_SSL_BEGIN
_SSBASEL_BEGIN


static unsigned int s_cvt_win_ue2a_size(const char *src)
{
	if(src == NULL)
		return 1;
	wchar_t *middest = cvt_ue2w(src);
	unsigned int expected_dest_size = win_cvt_w2a_size(win_cvt_default_pagecode(), middest);
	cvt_free(middest);
	return expected_dest_size;
}

static void s_cvt_win_ue2a(const char *src, char *dst, unsigned int destsize)
{
	wchar_t *middest = cvt_ue2w(src);
	cvt_w2a_s(middest, dst, destsize);
	cvt_free(middest);
}

static unsigned int s_cvt_win_a2ue_size(const char *src)
{
	if(src == NULL) return 1;
	wchar_t *middest = cvt_a2w(src);
	unsigned int expected_dest_size = win_cvt_w2a_size(win_utf8_codepage(), middest);
	cvt_free(middest);
	return expected_dest_size;
}

static void s_cvt_win_a2ue(const char *src, char *dst, unsigned int destsize)
{
	wchar_t *middest = cvt_a2w(src);
	cvt_w2ue_s(middest, dst, destsize);
	cvt_free(middest);
}
//
SSCVT_API bool cvt_a2w_s(const char *src, wchar_t *dst, unsigned int destsize)
{
	if(dst == NULL) return false;
	unsigned int expected_dst_size = win_cvt_a2w_size(win_cvt_default_pagecode(), src);
	if(destsize < expected_dst_size)
		return false;
	win_cvt_a2w(win_cvt_default_pagecode(), src, dst, destsize);
	return true;
}

SSCVT_API wchar_t* cvt_a2w(const char *str)
{
	unsigned int expected_dest_size = win_cvt_a2w_size(win_cvt_default_pagecode(), str);
	wchar_t *dest = (wchar_t*)malloc(expected_dest_size * sizeof(wchar_t));
	cvt_a2w_s(str, dest, expected_dest_size);
	return dest;
}

SSCVT_API bool cvt_w2a_s(const wchar_t *src, char *dst, unsigned int destsize)
{
	unsigned int expected_dst_size = win_cvt_w2a_size(win_cvt_default_pagecode(), src);
	if(dst == NULL || destsize < expected_dst_size)
		return false;
	win_cvt_w2a(win_cvt_default_pagecode(), src, dst, destsize);
	return true;
}

SSCVT_API char* cvt_w2a(const wchar_t *str)
{
	unsigned int expected_dest_size = win_cvt_w2a_size(win_cvt_default_pagecode(), str);
	char *dest = (char*)malloc(expected_dest_size * sizeof(char));
	cvt_w2a_s(str, dest, expected_dest_size);
	return dest;
}

SSCVT_API bool cvt_ue2w_s(const char *src, wchar_t *dst, unsigned int destsize)
{
	unsigned int expected_dst_size = win_cvt_a2w_size(win_utf8_codepage(), src);
	if(dst == NULL || destsize < expected_dst_size)
		return false;
	win_cvt_a2w(win_utf8_codepage(), src, dst, destsize);
	return true;
}

SSCVT_API wchar_t* cvt_ue2w(const char *str)
{
	unsigned int expected_dest_size = win_cvt_a2w_size(win_utf8_codepage(), str);
	wchar_t *dest = (wchar_t*)malloc(expected_dest_size * sizeof(wchar_t));
	cvt_ue2w_s(str, dest, expected_dest_size);
	return dest;
}

SSCVT_API bool cvt_w2ue_s(const wchar_t *src, char *dst, unsigned int destsize)
{
	unsigned int expected_dst_size = win_cvt_w2a_size(win_utf8_codepage(), src);
	if(dst == NULL || destsize < expected_dst_size)
		return false;
	win_cvt_w2a(win_utf8_codepage(), src, dst, destsize);
	return true;
}

SSCVT_API char* cvt_w2ue(const wchar_t *str)
{
	unsigned int expected_dest_size = win_cvt_w2a_size(win_utf8_codepage(), str);
	char *dest = (char*)malloc(expected_dest_size * sizeof(char));
	cvt_w2ue_s(str, dest, expected_dest_size);
	return dest;
}

SSCVT_API bool cvt_ue2a_s(const char *src, char *dst, unsigned int destsize)
{
	unsigned int expected_dst_size = s_cvt_win_ue2a_size(src);
	if(dst == NULL || destsize < expected_dst_size)
		return false;
	s_cvt_win_ue2a(src, dst, destsize);
	return true;
}

SSCVT_API char* cvt_ue2a(const char *str)
{
	unsigned int expected_dest_size = s_cvt_win_ue2a_size(str);
	char *dest = (char*)malloc(expected_dest_size * sizeof(char));
	cvt_ue2a_s(str, dest, expected_dest_size);
	return dest;
}

SSCVT_API bool cvt_a2ue_s(const char *src, char *dst, unsigned int destsize)
{
	unsigned int expected_dst_size = s_cvt_win_a2ue_size(src);
	if(dst == NULL || destsize < expected_dst_size)
		return false;
	s_cvt_win_a2ue(src, dst, destsize);
	return true;
}

SSCVT_API char* cvt_a2ue(const char *str)
{
	unsigned int expected_dest_size = s_cvt_win_a2ue_size(str);
	char *dest = (char*)malloc(expected_dest_size * sizeof(char));
	cvt_a2ue_s(str, dest, expected_dest_size);
	return dest;
}

SSCVT_API wchar_t* cvt_w2w(const wchar_t *str)
{
	if(str == NULL) str = L"";
	unsigned int expected_dest_size = wcslen(str) + 1;
	wchar_t *dest = (wchar_t*)malloc(expected_dest_size * sizeof(wchar_t));
	memcpy(dest, str, expected_dest_size * sizeof(wchar_t));
	return dest;
}

SSCVT_API char* cvt_a2a(const char *str)
{
	if(str == NULL) str = "";
	unsigned int expected_dest_size = strlen(str) + 1;
	char *dest = (char*)malloc(expected_dest_size);
	memcpy(dest, str, expected_dest_size);
	return dest;
}

SSCVT_API char* cvt_ue2ue(const char *str)
{
	return cvt_a2a(str);
}

SSCVT_API void cvt_free(void *p)
{
	free(p);
}

SSCVT_API bool cvt_checkutf8(const char *str)
{
	if(NULL == str) return false;

	const unsigned char * bytes = (const unsigned char *)str;
	while(*bytes)
	{
		if( (// ASCII
			// use bytes[0] <= 0x7F to allow ASCII control characters
			bytes[0] == 0x09 ||
			bytes[0] == 0x0A ||
			bytes[0] == 0x0D ||
			(0x20 <= bytes[0] && bytes[0] <= 0x7E)
			)
			) {
				bytes += 1;
				continue;
		}

		if( (// non-overlong 2-byte
			(0xC2 <= bytes[0] && bytes[0] <= 0xDF) &&
			(0x80 <= bytes[1] && bytes[1] <= 0xBF)
			)
			) {
				bytes += 2;
				continue;
		}

		if( (// excluding overlongs
			bytes[0] == 0xE0 &&
			(0xA0 <= bytes[1] && bytes[1] <= 0xBF) &&
			(0x80 <= bytes[2] && bytes[2] <= 0xBF)
			) ||
			(// straight 3-byte
			((0xE1 <= bytes[0] && bytes[0] <= 0xEC) ||
			bytes[0] == 0xEE ||
			bytes[0] == 0xEF) &&
			(0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
			(0x80 <= bytes[2] && bytes[2] <= 0xBF)
			) ||
			(// excluding surrogates
			bytes[0] == 0xED &&
			(0x80 <= bytes[1] && bytes[1] <= 0x9F) &&
			(0x80 <= bytes[2] && bytes[2] <= 0xBF)
			)
			) {
				bytes += 3;
				continue;
		}

		if( (// planes 1-3
			bytes[0] == 0xF0 &&
			(0x90 <= bytes[1] && bytes[1] <= 0xBF) &&
			(0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
			(0x80 <= bytes[3] && bytes[3] <= 0xBF)
			) ||
			(// planes 4-15
			(0xF1 <= bytes[0] && bytes[0] <= 0xF3) &&
			(0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
			(0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
			(0x80 <= bytes[3] && bytes[3] <= 0xBF)
			) ||
			(// plane 16
			bytes[0] == 0xF4 &&
			(0x80 <= bytes[1] && bytes[1] <= 0x8F) &&
			(0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
			(0x80 <= bytes[3] && bytes[3] <= 0xBF)
			)
			) {
				bytes += 4;
				continue;
		}

		return false;
	}

	return true;
}


_SSBASEL_END
	_SSL_END

